package com.example.flowable.service.impl;

import com.example.annotations.Autowired;
import com.example.annotations.Service;
import com.example.flowable.FLowableUtils;
import com.example.flowable.config.ExpressionCmd;
import com.example.flowable.service.FlowableService;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;

import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.task.api.Task;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Classname FLowableServiceImpl
 * @Description TODO
 * @Date 2022/8/22 15:02
 * @Created by XiongXiong
 * @Author: X.I.O
 */
@Service
public class FLowableServiceImpl implements FlowableService {

    @Autowired
    FLowableUtils fLowableUtils;

    @Override
    public void deployment() throws FileNotFoundException {
        ProcessEngine processEngine=fLowableUtils.getProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("员工请假流程.bpmn20.xml")
                .deploy();

        //查询流程版本
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();

        //获取流程的key
        String processKey=processDefinition.getKey();

        //启动一个实例
        RuntimeService runtimeService = processEngine.getRuntimeService();
        Map<String, Object> variables = new HashMap<>();
        variables.put("employee", "Tom");

        //设置发起人
        Authentication.setAuthenticatedUserId("fqr");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("EMP_PROC",variables);
        Authentication.setAuthenticatedUserId(null);

        //创建流程任务
        TaskService taskService = processEngine.getTaskService();
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();

        //提交任务
        taskService.complete(tasks.get(0).getId(), variables);

        String processInstanceId="";
        String currentActivityTaskId="";
        String newActivityTaskId="";
        //人为跳转任务
        runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
               .moveActivityIdTo(currentActivityTaskId, newActivityTaskId).changeState();

        // 申领任务
        taskService.claim(tasks.get(0).getId(), "myte");
        // 完成
        Map<String, Object> variables1 = new HashMap<>();
        variables.put("command","agree"); //携带变量，用于网关流程的条件判定，这里的条件是同意
        taskService.complete(tasks.get(0).getId(), variables1);

        //查询流程启动的所有实例列表
        List<Execution> executions = runtimeService.createExecutionQuery().processDefinitionKey("leave_approval").list();

        //删除流程实例
        String deleteReason="删除原因";
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason); //删除实例

        //查询部署的流程定义
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave_approval").list();


        //先部署流程模板
        //tenantId 租户id
        String companyName="";
        Deployment deploy = repositoryService
                .createDeployment()
                .tenantId(companyName)
                .addInputStream("demo.bpmn20.xml", new FileInputStream(new File("classpath:demo.bpmn20.xml")))
                .deploy();

        //再查看流程对象
        ProcessDefinition processDef = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("key")
                .processDefinitionTenantId(companyName)
                .singleResult();


    }

    @Override
    public Deployment  deploymentByFile(String file) throws FileNotFoundException {
        File f=new File(file);
        ProcessEngine processEngine=fLowableUtils.getProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deploy = repositoryService
                .createDeployment()
                .addInputStream(f.getName(), new FileInputStream(f))
                .deploy();
        return deploy;
    }


    @Override
    public ProcessDefinition getProcessDefinitionByDeployId(String deployId) {
        ProcessEngine processEngine=fLowableUtils.getProcessEngine();
        RepositoryService repositoryService=processEngine.getRepositoryService();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployId)
                .singleResult();
        return processDefinition;
    }

    @Override
    public ProcessInstance createProcessInstance(String deployId, String title, String uid, Map<String, Object> object) {

        ProcessDefinition processDefinition=getProcessDefinitionByDeployId(deployId);
        ProcessEngine processEngine=fLowableUtils.getProcessEngine();
        RuntimeService runtimeService=processEngine.getRuntimeService();
        //设置发起人
        Authentication.setAuthenticatedUserId(uid);

        ProcessInstanceBuilder processInstanceBuilder=runtimeService.createProcessInstanceBuilder();
        processInstanceBuilder.processDefinitionId(processDefinition.getId());
        processInstanceBuilder.name(title);
        processInstanceBuilder.variables(object);
        ProcessInstance processInstance=processInstanceBuilder.start();
        Authentication.setAuthenticatedUserId(null);
        return processInstance;
    }

    @Override
    public List<Process> getProcessListByDeployId(String deployId) {
        ProcessDefinition processDefinition=getProcessDefinitionByDeployId(deployId);

        ProcessEngine processEngine=fLowableUtils.getProcessEngine();
        RepositoryService repositoryService=processEngine.getRepositoryService();

        List<Process> processes = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses();
        return processes;
    }

    @Override
    public void submitTask(String taskId, String uid, String msg) {
        TaskService taskService = fLowableUtils.getProcessEngine().getTaskService();
        Task task=taskService.createTaskQuery().taskId(taskId).singleResult();
        if(task != null){
          //  taskService.claim(taskId, uid);
            // 完成
            Map<String, Object> variables = new HashMap<>();
            variables.put("uid",uid); //携带变量，用于网关流程的条件判定，这里的条件是同意
            //添加留言
            taskService.addComment(task.getId(),task.getProcessInstanceId(),msg);
            //完成任务
            taskService.complete(taskId, variables);
        }
    }

    @Override
    public List<Task> getTaskListByProcessInstanceId(String processInstanceId) {
        TaskService taskService=fLowableUtils.getTaskService();
        return taskService.createTaskQuery().processInstanceId(processInstanceId).list();
    }

    @Override
    public void dealGetWay(FlowElement flowElement, Map data) {
        List<SequenceFlow> targetFlows=((ExclusiveGateway)flowElement).getOutgoingFlows();
        // 循环每个网关分支
        for(SequenceFlow sequenceFlow : targetFlows) {
            // 获取下一个网关和节点数据
            FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
            // 网关数据不为空
            if (sequenceFlow.getConditionExpression() != null && !"".equals(sequenceFlow.getConditionExpression())) {
                //计算连接线上的表达式
                //  Object result = managementService.executeCommand(new ExpressionEvaluateUtil(sequenceFlow.getConditionExpression(), data));
                System.out.println("排他网关中线条: id=" + sequenceFlow.getId() + ",name=" + sequenceFlow.getName() + ",result=,ConditionExpression=" + sequenceFlow.getConditionExpression());
                // 获取网关判断条件
                String conditionExpression = null;
                if (sequenceFlow.getConditionExpression() != null) {
                    conditionExpression = sequenceFlow.getConditionExpression().substring(sequenceFlow.getConditionExpression().indexOf("==") + 2,
                            sequenceFlow.getConditionExpression().indexOf("}"));
                }
                System.out.println("截取后的选择条件: id=" + conditionExpression);
            }
            // 网关的下个节点是用户节点
            if (targetFlowElement instanceof UserTask) {
                // 判断是否是会签
                UserTask userTask = (UserTask) targetFlowElement;
                if (userTask.getBehavior() instanceof ParallelMultiInstanceBehavior) {
                    ParallelMultiInstanceBehavior behavior = (ParallelMultiInstanceBehavior) userTask.getBehavior();
                    if (behavior != null && behavior.getCollectionExpression() != null) {
                        System.out.println("当前节点是会签");
                    }
                } else {
                    System.out.println("当前节点不是会签");
                }
                System.out.println("排他网关的下一节点是UserTask: id=" + targetFlowElement.getId() + ",name=" + targetFlowElement.getName());
                System.out.println("排他网关的下一节点CandidateUsers候选者" + ((UserTask) targetFlowElement).getCandidateUsers());

            } else if (targetFlowElement instanceof EndEvent) {
                System.out.println("排他网关的下一节点是EndEvent: 结束节点");
            } else if (targetFlowElement instanceof ServiceTask) {
                System.out.println("排他网关的下一节点是ServiceTask: 内部方法");
            } else if (targetFlowElement instanceof ExclusiveGateway) {
                //  setExclusiveGateway(targetFlowElement,data);
            } else if (targetFlowElement instanceof SubProcess) {
                System.out.println("排他网关的下一节点是SubProcess: 内部子流程");
            }
        }
    }

    @Override
    public void changeProcessInstancsNode(String processId, String currentNodeId, String nodeId) {
        RuntimeService runtimeService=fLowableUtils.getRuntimeService();
        runtimeService.createChangeActivityStateBuilder().processInstanceId(processId)
                .moveActivityIdTo(currentNodeId, nodeId).changeState();
    }


    @Override
    public ProcessInstance getProcessInstanceById(String processInstanceId) {
        RuntimeService runtimeService = fLowableUtils.getRuntimeService();
        ProcessInstance processInstance=runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        return processInstance;
    }

    @Override
    public List<FlowElement> getAllNodesByProcessId(String processId, Map<String, Object> objects) {
        return calApprovePath(processId, objects);
    }


    /**
     * 1. 首先拿到BpmnModel，所有流程定义信息都可以通过BpmnModel获取；若流程尚未发起，则用modelId查询最新部署的流程定义数据；
     * 若流程已经发起，可以通过流程实例的processDefinitionId查询流程定义的历史数据。
     * @param variableMap 流程变量，用于计算条件分支
     */
    private List<FlowElement> calApprovePath(String processInstanceId, Map<String, Object> variableMap){
        BpmnModel bpmnModel=null;
        if(StringUtils.isNotBlank(processInstanceId)){
            RuntimeService runtimeService=fLowableUtils.getRuntimeService();
            RepositoryService repositoryService=fLowableUtils.getRepositoryService();
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        }
        if(bpmnModel==null){
            return Collections.emptyList();
        }
        Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
        for (FlowElement flowElement : flowElements) {
            System.out.println(flowElement.getName()+" 5555555555");
            System.out.println(flowElement.getId());
        }
        List<FlowElement> passElements = new ArrayList<>();
        this.dueStartElement(passElements, flowElements, variableMap,processInstanceId);
        return passElements;
    }

    private void dueUserTaskElement(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variableMap,String processId){
        passElements.add(curFlowElement);
        List<SequenceFlow> outgoingFlows = ((UserTask) curFlowElement).getOutgoingFlows();
        String targetRef = outgoingFlows.get(0).getTargetRef();
        if (outgoingFlows.size() > 1) {
            // 找到表达式成立的sequenceFlow
            SequenceFlow sequenceFlow = getSequenceFlow(variableMap, outgoingFlows,processId);
            targetRef = sequenceFlow.getTargetRef();
        }
        // 根据ID找到FlowElement
        FlowElement targetElement = getFlowElement(flowElements, targetRef);
        this.getPassElementList(passElements, flowElements, targetElement, variableMap,processId);
    }



    private void dueExclusiveGateway(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variableMap,String processId){
        // 获取符合条件的sequenceFlow的目标FlowElement
        List<SequenceFlow> exclusiveGatewayOutgoingFlows = ((ExclusiveGateway) curFlowElement).getOutgoingFlows();
        flowElements.remove(curFlowElement);
        // 找到表达式成立的sequenceFlow
        SequenceFlow sequenceFlow = getSequenceFlow(variableMap, exclusiveGatewayOutgoingFlows,processId);
        // 根据ID找到FlowElement
        FlowElement targetElement = getFlowElement(flowElements, sequenceFlow.getTargetRef());
        this.getPassElementList(passElements, flowElements, targetElement, variableMap,processId);
    }

    private void dueParallelGateway(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variableMap,String processId){
        FlowElement targetElement;
        List<SequenceFlow> parallelGatewayOutgoingFlows = ((ParallelGateway) curFlowElement).getOutgoingFlows();
        for(SequenceFlow sequenceFlow : parallelGatewayOutgoingFlows){
            targetElement = getFlowElement(flowElements, sequenceFlow.getTargetRef());
            this.getPassElementList(passElements, flowElements, targetElement, variableMap,processId);
        }
    }



    /**
     * 3. 我只用到了UserTask、ExclusiveGateway、ParallelGateway，所以代码里只列举了这三种，如果用到了其他的，可以再自己补充
     */
    private void getPassElementList(List<FlowElement> passElements, Collection<FlowElement> flowElements, FlowElement curFlowElement, Map<String, Object> variableMap,String processId){
        // 任务节点
        if (curFlowElement instanceof UserTask) {
            this.dueUserTaskElement(passElements, flowElements, curFlowElement, variableMap,processId);
            return;
        }
        // 排他网关
        if (curFlowElement instanceof ExclusiveGateway) {
            this.dueExclusiveGateway(passElements, flowElements, curFlowElement, variableMap,processId);
            return;
        }
        // 并行网关
        if(curFlowElement instanceof ParallelGateway){
            this.dueParallelGateway(passElements, flowElements, curFlowElement, variableMap,processId);
        }
    }



    /**
     * 4. 根据传入的变量，计算出表达式成立的那一条SequenceFlow
     * @param variableMap
     * @param outgoingFlows
     * @return
     */
    private SequenceFlow getSequenceFlow(Map<String, Object> variableMap, List<SequenceFlow> outgoingFlows,String processId) {
        Optional<SequenceFlow> sequenceFlowOpt = outgoingFlows.stream().filter(item -> {
            try {
                return this.getElValue(item.getConditionExpression(), variableMap,processId);
            } catch (Exception e) {
                System.out.println(e.getMessage()+"异常");
                return false;
            }
        }).findFirst();
        return sequenceFlowOpt.orElse(outgoingFlows.get(0));
    }

    /**
     * 2. 找到开始节点，通过它的目标节点，然后再不断往下找。
     */
    private void dueStartElement(List<FlowElement> passElements, Collection<FlowElement> flowElements, Map<String, Object> variableMap,String processId){
        Optional<FlowElement> startElementOpt = flowElements.stream().filter(flowElement -> flowElement instanceof StartEvent).findFirst();
        startElementOpt.ifPresent(startElement -> {
            flowElements.remove(startElement);
            List<SequenceFlow> outgoingFlows = ((StartEvent) startElement).getOutgoingFlows();
            String targetRef = outgoingFlows.get(0).getTargetRef();
            // 根据ID找到FlowElement
            FlowElement targetElementOfStartElement = getFlowElement(flowElements, targetRef);
            if (targetElementOfStartElement instanceof UserTask) {
                this.getPassElementList(passElements, flowElements, targetElementOfStartElement, variableMap,processId);
            }
        });
    }







    /***
     * 根据ID找到FlowElement
     * @param flowElements
     * @param targetRef
     * @return
     */
    private FlowElement getFlowElement(Collection<FlowElement> flowElements, String targetRef) {
        List<FlowElement> targetFlowElements = flowElements.stream().filter(
                flowElement -> !StringUtils.isEmpty(flowElement.getId()) && flowElement.getId().equals(targetRef)
        ).collect(Collectors.toList());
        if (targetFlowElements.size() > 0) {
            return targetFlowElements.get(0);
        }
        return null;
    }



    private boolean getElValue(String exp, Map<String, Object> variableMap,String processId){
        ManagementService managementService=fLowableUtils.getProcessEngine().getManagementService();
        RuntimeService runtimeService=fLowableUtils.getProcessEngine().getRuntimeService();
        ProcessEngineConfigurationImpl processEngineConfiguration=(ProcessEngineConfigurationImpl)fLowableUtils.getProcessEngine().getProcessEngineConfiguration();
        return managementService.executeCommand(new ExpressionCmd(runtimeService, processEngineConfiguration, processId, exp, variableMap));
    }

}
